package aclInfoCollector.serialisator;

import java.io.File;
import java.util.ArrayList;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;

import aclInfoCollector.utils.specialString.StringInterface;
import aclInfoCollector.utils.specialString.StringOperation;
import aclInfoCollector.utils.specialString.StringParameter;



import cocola.Class;
import cocola.Cocola;
import cocola.CocolaFactory;
import cocola.CocolaPackage;
import cocola.Component;
import cocola.DirectedInterface;
import cocola.Interface;
import cocola.InterfaceRole;
import cocola.Operation;
import cocola.Parameter;
import cocola.ParameterDirection;
import cocola.Primitive;

/**
 * La classe Serialisator permet d'ecrire le xmi regroupant les informations de
 * infoBinder
 * 
 * @version 1.0
 *@author Bendavid Olivier
 *@author Haderer Nicolas
 */
public class Serialisator {

	public static void main(String args[]) {
		System.out.println("debut");
		System.out.println();
		Serialisator s = new Serialisator("fractalexplorer");

		System.out.println();
		System.out.println("fin");
	}

	private Cocola coco;
	private InfoBinder infoBinder;
	private String xmiPath;

	public Serialisator() {
		coco = CocolaFactory.eINSTANCE.createCocola();
		infoBinder = new InfoBinder();
		xmiPath = "xmi.xmi";
	}
	
	
	public Serialisator(String path) {
		coco = CocolaFactory.eINSTANCE.createCocola();
		infoBinder = new InfoBinder();
		xmiPath = "xmi.xmi";
		generate(path);
	}
	
	
	public Serialisator(String pathIN,String pathOUT) {
		coco = CocolaFactory.eINSTANCE.createCocola();
		infoBinder = new InfoBinder();
		generate(pathIN,pathOUT);
	}
	
	/**
	 * ajoute une classe en verifiant qu'il n'y ait pas de doublons
	 * @param s
	 */

	@SuppressWarnings("unchecked")
	public void addClass(String s) {
		Class c = CocolaFactory.eINSTANCE.createClass();
		c.setName(s);
		if (!isClassAlreadyHere(c) && !infoBinder.getPrimitives().contains(s)) {
			coco.getClasses().add(c);
		}
	}

	
	/**
	 * ajoute les classes de infobinder
	 * @param s
	 */
	public void addClasses() {
		for (String s : infoBinder.getClasses()) {
			addClass(s);
		}

	}

	
	/**
	 * ajoute les classes et leurs super types
	 * 
	 */
	public void addClassesWithSuperTypes() {
		for (String s : infoBinder.getClassesWithSuperTypes().keySet()) {
			addClasseWithSuperTypes(s, infoBinder.getClassesWithSuperTypes()
					.get(s));
		}
	}

	/**
	 *
	 * ajoute une classe et ses super types en verifiant qu'il n'y ait pas de doublons
	 * @param s
	 * @param a
	 */
	@SuppressWarnings("unchecked")
	public void addClasseWithSuperTypes(String s, ArrayList<String> a) {
		Class c = CocolaFactory.eINSTANCE.createClass();
		c.setName(s);
		if (!isClassAlreadyHere(c)) {
			coco.getClasses().add(c);
		}

		for (String sa : a) {
			Class n = CocolaFactory.eINSTANCE.createClass();
			n.setName(sa);
			if (!isClassAlreadyHere(n)) {
				coco.getClasses().add(n);
			}
			if (isPrimitive(sa)) {
				((Class) coco.getClasses().get(getCocolaIndexClassByName(s)))
						.getSuperTypes().add(
								coco.getPrimitives().get(
										getCocolaIndexPrimitiveByName(sa)));
			} else {
				((Class) coco.getClasses().get(getCocolaIndexClassByName(s)))
						.getSuperTypes().add(
								coco.getClasses().get(
										getCocolaIndexClassByName(sa)));

			}

		}

	}
	
	/**
	 * 
	 * ajoute un composant en verifiant qu'il n'y ait pas de doublons
	 * @param name
	 */

	@SuppressWarnings("unchecked")
	public void addComponent(String name) {
		Component c = CocolaFactory.eINSTANCE.createComponent();
		c.setName(name);
		if (!isComponentAlreadyHere(c)) {
			coco.getComponents().add(c);
		}
	}

	
	/**
	 * 
	 * ajoute les composants
	 * @param name
	 */
	
	public void addComponents() {
		for (String s : infoBinder.getComponents()) {
			addComponent(s);
		}
	}

	
	/**
	 * ajoute les composants et leurs interfaces
	 */
	public void addComponentsWithInterfaces() {
		for (String s : infoBinder.getComponentsWithInterfaces().keySet()) {
			addComponentWithInterface(s, infoBinder
					.getComponentsWithInterfaces().get(s));
		}

	}

	
	
	/**
	 * ajoute le composant name et ses interfaces en verifiant les doublons
	 * @param name
	 * @param a
	 */
	@SuppressWarnings("unchecked")
	public void addComponentWithInterface(String name,
			ArrayList<StringInterface> a) {
		addComponent(name);
		if (!a.isEmpty()) {
			for (StringInterface s : a) {
				addInterface(s.getName());

				DirectedInterface id = CocolaFactory.eINSTANCE
						.createDirectedInterface();
				if (s.getRole().equals("client")) {
					id.setRole(InterfaceRole.REQUIRED);
				} else if (s.getRole().equals("server")) {
					id.setRole(InterfaceRole.PROVIDED);
				}
				id.setInterface((Interface) coco.getInterfaces().get(
						getCocolaIndexInterfaceByName(s.getName())));
				((Component) coco.getComponents().get(
						getCocolaIndexComponentByName(name))).getInterfaces()
						.add(id);
			}
		}

	}

	
	
	
	/**
	 * ajoute les composants et leurs super composants
	 */
	@SuppressWarnings("unchecked")
	public void addComponentWithSuperComp() {
		for (String s : infoBinder.getComponentsWithSuperComps().keySet()) {
			addComponent(s);
			if (!infoBinder.getComponentsWithSuperComps().get(s).isEmpty()) {
				for (String sd : infoBinder.getComponentsWithSuperComps()
						.get(s)) {
					addComponent(sd);
					((Component) coco.getComponents().get(
							getCocolaIndexComponentByName(s))).getSuperTypes()
							.add(
									coco.getComponents().get(
											getCocolaIndexComponentByName(sd)));
				}
			}
		}

	}

	
	/**
	 * ajoute un interface en verifiant les doublons
	 * @param name
	 */
	@SuppressWarnings("unchecked")
	public void addInterface(String name) {
		Interface c = CocolaFactory.eINSTANCE.createInterface();
		c.setName(name);
		if (!isInterfaceAlreadyHere(c)) {
			coco.getInterfaces().add(c);
		}
	}

	
	/**
	 * ajoute les interfaces
	 */
	public void addInterfaces() {
		for (StringInterface s : infoBinder.getInterfaces()) {
			addInterface(s.getName());
		}
	}

	
	/**
	 * ajoute les interfaces avec leurs operations
	 */
	public void addInterfacesWithOperations() {
		for (String s : infoBinder.getInterfacesWithOperations().keySet()) {
			addInterfaceWithOperation(s, infoBinder
					.getInterfacesWithOperations().get(s));
		}
	}

	
	
	/**
	 * ajoute les interfaces avec leurs super interface
	 */
	@SuppressWarnings("unchecked")
	public void addInterfacesWithSuperInterfaces() {
		for (String s : infoBinder.getInterfacesWithSuperInterFaces().keySet()) {
			addInterface(s);
			if (!infoBinder.getInterfacesWithSuperInterFaces().get(s).isEmpty()) {
				for (String sd : infoBinder.getInterfacesWithSuperInterFaces()
						.get(s)) {
					addInterface(sd);
					((Interface) coco.getInterfaces().get(
							getCocolaIndexInterfaceByName(s))).getSuperTypes()
							.add(
									(Interface) coco.getInterfaces().get(
											getCocolaIndexInterfaceByName(sd)));
				}
			}
		}
	}

	
	/**
	 * ajoute une interface et ses operations en verifiant qu'il n'y ait pas de doublons
	 * @param name
	 * @param a
	 */
	@SuppressWarnings("unchecked")
	public void addInterfaceWithOperation(String name,
			ArrayList<StringOperation> a) {
		Interface i = CocolaFactory.eINSTANCE.createInterface();
		i.setName(name);
		if (!a.isEmpty()) {
			for (StringOperation strop : a) {
				Operation o = CocolaFactory.eINSTANCE.createOperation();
				o.setName(strop.getName());
				for (StringParameter s : strop.getParamsTypes()) {

					Class c = CocolaFactory.eINSTANCE.createClass();
					c.setName(s.getType());
					System.out.println(s.getType());
					Parameter p = CocolaFactory.eINSTANCE.createParameter();
					p.setName(s.getName());

					if (!isClassAlreadyHere(c)) {
						coco.getClasses().add(c);
						p.setType(c);
					} else {
						if (!isPrimitive(c.getName().toString())) {
							p.setType((Class) (coco.getClasses()
									.get(getCocolaIndexClassByName(s.getType()
											.toString()))));
						} else {
							p.setType((Primitive) (coco.getPrimitives()
									.get(getCocolaIndexPrimitiveByName(s
											.getType().toString()))));
						}
					}
					p.setDirection(ParameterDirection.IN);
					o.getParameters().add(p);
				}
				Parameter p = CocolaFactory.eINSTANCE.createParameter();
				p.setName(strop.getReturnType().getName());
				Class c = CocolaFactory.eINSTANCE.createClass();
				c.setName(strop.getReturnType().getType());
				if (!isClassAlreadyHere(c)) {
					coco.getClasses().add(c);
					p.setType(c);
				} else {
					if (!isPrimitive(c.getName().toString())) {
						p
								.setType((Class) (coco.getClasses()
										.get(getCocolaIndexClassByName(strop
												.getReturnType().getType()
												.toString()))));
					} else {
						p
								.setType((Primitive) (coco.getPrimitives()
										.get(getCocolaIndexPrimitiveByName(strop
												.getReturnType().getType()
												.toString()))));
					}
				}

				p.setDirection(ParameterDirection.OUT);
				o.getParameters().add(p);
				o.setName(strop.getName());
				i.getOperations().add(o);
			}

			if (!isInterfaceAlreadyHere(i)) {
				coco.getInterfaces().add(i);
			}

			else {
				Interface itemp = (Interface) coco.getInterfaces().get(
						getCocolaIndexInterfaceByName(i.getName().toString()));
				for (int g = 0; g < i.getOperations().size(); g++) {
					Operation op = (Operation) i.getOperations().get(g);

					if (!itemp.getOperations().contains(
							i.getOperations().get(g))) {
						((Interface) coco.getInterfaces().get(
								getCocolaIndexInterfaceByName(i.getName()
										.toString()))).getOperations().add(op);
					}
				}
			}

		}
	}

	
	/**
	 * ajoute une primitive de nom s en verifiant qu'elle n'existe pas déja
	 * @param s
	 */
	@SuppressWarnings("unchecked")
	public void addPrimitive(String s) {
		Primitive p = CocolaFactory.eINSTANCE.createPrimitive();
		p.setName(s);
		if (!isPrimitiveAlreadyHere(p)) {
			coco.getPrimitives().add(p);
		}
	}
	
	/**
	 * ajoute les primitives
	 */
	public void addPrimitives() {
		for (String s : infoBinder.getPrimitives()) {
			addPrimitive(s);
		}
	}
	
	
	/**
	 * ajoute le super type void au type qui n'ont pas de super type
	 */

	@SuppressWarnings("unchecked")
	public void addSuperTypeVoid() {
		if(!coco.getPrimitives().contains("void"))
		{
			Primitive p = CocolaFactory.eINSTANCE.createPrimitive();
			p.setName("void");
			coco.getPrimitives().add(p);
		}
		for (Object c : coco.getClasses()) {
			if (((Class) c).getSuperTypes().isEmpty()) {
				((Class) coco.getClasses().get(
						getCocolaIndexClassByName(((Class) c).getName()
								.toString()))).getSuperTypes().add(
						coco.getPrimitives().get(
								getCocolaIndexPrimitiveByName("void")));
			}
		}
	}

	
	/**
	 * genere le xmi associé aux informations contenues dans infobinder
	 * @param path
	 */
	public void generate(String path) {
		infoBinder = new InfoBinder(path);

		addPrimitives();
		addClasses();
		addInterfaces();
		addComponents();
		addInterfacesWithOperations();
		addComponentsWithInterfaces();
		addComponentWithSuperComp();
		//addInterfacesWithSuperInterfaces();
		addClassesWithSuperTypes();
		addSuperTypeVoid();
		generateXMIFile("XMI.xmi");
	}

	
	/**
	 * genere le xmi associé aux informations contenues dans infobinder
	 * @param path
	 */
	public void generate(String path, String pathOUT) {
		infoBinder = new InfoBinder(path);
		xmiPath = pathOUT;
		addPrimitives();
		addClasses();
		addInterfaces();
		addComponents();
		addInterfacesWithOperations();
		addComponentsWithInterfaces();
		addComponentWithSuperComp();
		addInterfacesWithSuperInterfaces();
		addClassesWithSuperTypes();
		addSuperTypeVoid();
		generateXMIFile(pathOUT);
	}

	
	
	/**
	 * 
	 * genere le xmi associé aux informations contenues dans infobinder
	 * suivant differents mode
	 * 
	 * mode 0 = safe
	 * mode 1 = risk
	 * 
	 * 
	 * @param path
	 * @param mode
	 */
	public void generate(String path, int mode) {
		infoBinder = new InfoBinder(path);

		switch (mode) {
			case '0' : {
				addPrimitives();
				addClasses();
				addInterfaces();
				addComponents();
				addInterfacesWithOperations();
				addComponentsWithInterfaces();
				addComponentWithSuperComp();
				// addInterfacesWithSuperInterfaces();
				addClassesWithSuperTypes();
				addSuperTypeVoid();
				generateXMIFile("XMI.xmi");
			}
				break;

			case '1' : {
				addPrimitives();
				addClasses();
				addInterfaces();
				addComponents();
				addInterfacesWithOperations();
				addComponentsWithInterfaces();
				addComponentWithSuperComp();
				addInterfacesWithSuperInterfaces();
				addClassesWithSuperTypes();
				addSuperTypeVoid();
				generateXMIFile("XMI.xmi");
			}
				break;

			default :
				System.out.println("ce mode n'existe pas");
				break;

		}
	}
	
	
	
	
	
	
	

	
	/**
	 * genere le fichier xmi associée aux informations contenue dans le dossier path
	 * @param path
	 */
	public void generateXMIFile(String path) {
		ResourceSet resourceSet = new ResourceSetImpl();
		resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap()
				.put(Resource.Factory.Registry.DEFAULT_EXTENSION,
						new XMIResourceFactoryImpl());
		resourceSet.getPackageRegistry().put(CocolaPackage.eNS_URI,
				CocolaPackage.eINSTANCE);
		URI uri = URI.createFileURI(new File(path).getAbsolutePath());
		Resource resource = resourceSet.createResource(uri);
		resource.getContents().add(coco);
		try {
			resource.save(null);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public int getCocolaIndexClassByName(String s) {
		int place = -1;
		for (int c = 0; c < coco.getClasses().size(); c++) {
			if (((Class) coco.getClasses().get(c)).getName().equals(s)) {
				place = c;
			}
		}
		return place;
	}

	public int getCocolaIndexComponentByName(String s) {
		int place = -1;
		for (int c = 0; c < coco.getComponents().size(); c++) {
			if (((Component) coco.getComponents().get(c)).getName().toString()
					.equals(s)) {
				place = c;
			}
		}
		return place;
	}

	public int getCocolaIndexInterfaceByName(String s) {
		int place = -1;
		for (int c = 0; c < coco.getInterfaces().size(); c++) {
			if (((Interface) coco.getInterfaces().get(c)).getName().equals(s)) {
				place = c;
			}
		}
		return place;
	}

	public int getCocolaIndexPrimitiveByName(String s) {
		int place = -1;
		for (int c = 0; c < coco.getPrimitives().size(); c++) {
			if (((Primitive) coco.getPrimitives().get(c)).getName().equals(s)) {
				place = c;
			}
		}
		return place;
	}

	public boolean isClassAlreadyHere(Class p) {

		for (Object px : coco.getClasses()) {
			if (((Class) px).getName().toString()
					.equals(p.getName().toString())
					|| isPrimitive(p.getName().toString())) {
				return true;
			}
		}
		return false;
	}

	public boolean isComponentAlreadyHere(Component p) {
		for (Object px : coco.getComponents()) {
			if (((Component) px).getName().toString().equals(
					p.getName().toString())) {
				return true;
			}
		}
		return false;
	}

	public boolean isInterfaceAlreadyHere(Interface p) {
		for (Object px : coco.getInterfaces()) {
			if (((Interface) px).getName().toString().equals(
					p.getName().toString())) {
				return true;
			}
		}
		return false;
	}

	// @SuppressWarnings("unchecked")
	// public void finishToBind()
	// {
	// for(String s: infoBinder.getClassesWithInterfaces().keySet())
	// {
	// if(getCocolaIndexComponentByName(s)!=-1)
	// {
	// System.out.println("test"+s);
	// for(String sd:infoBinder.getClassesWithInterfaces().get(s))
	// {
	// addInterface(sd);
	// DirectedInterface id = CocolaFactory.eINSTANCE.createDirectedInterface();
	// id.setRole(InterfaceRole.REQUIRED_LITERAL);
	// id.setInterface(((Interface)coco.getInterfaces().get(getCocolaIndexInterfaceByName(sd))));
	// ((Component)coco.getComponents().get(getCocolaIndexComponentByName(s))).getInterfaces().add(id);
	// }
	// }
	//			
	// }
	//				
	// }

	public boolean isPrimitive(String s) {
		for (Object px : coco.getPrimitives()) {
			if (((Primitive) px).getName().toString().equals(s)) {
				return true;
			}
		}
		return false;
	}

	public boolean isPrimitiveAlreadyHere(Primitive p) {
		for (Object px : coco.getPrimitives()) {
			if (((Primitive) px).getName().toString().equals(
					p.getName().toString())) {
				return true;
			}
		}
		return false;
	}

	public void printSummary() {
		System.out.println("nb classes : " + infoBinder.getClasses().size());
		System.out.println();
		System.out.println("nb composants : "
				+ infoBinder.getComponents().size());
		System.out.println();
		System.out.println("nb interfaces : "
				+ infoBinder.getInterfaces().size());
		System.out.println();
		System.out.println("nb primitives : "
				+ infoBinder.getPrimitives().size());

	}

	public InfoBinder getInfoBinder() {
		return infoBinder;
	}

	public void setInfoBinder(InfoBinder infoBinder) {
		this.infoBinder = infoBinder;
	}

	public String getXmiPath() {
		return xmiPath;
	}

}
